package top.mcwebsite.playandroid.common.network.impl.calladapter

import okhttp3.Request
import okio.Timeout
import retrofit2.*
import top.mcwebsite.playandroid.common.network.BizApiException
import top.mcwebsite.playandroid.common.network.NetworkApiException
import top.mcwebsite.playandroid.common.network.api.NetworkService
import top.mcwebsite.playandroid.common.network.impl.transform
import top.mcwebsite.playandroid.common.network.model.ApiResult
import top.mcwebsite.playandroid.common.network.model.RespModel
import top.mcwebsite.playandroid.common.network.noInternet
import top.mcwebsite.playandroid.common.network.unknown
import top.mcwebsite.playandroid.lib.servicemanager.core.ServiceManager
import java.io.IOException
import java.lang.RuntimeException
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type
import java.util.*
import java.util.concurrent.Executor


class ResultCallAdapterFactory : CallAdapter.Factory() {
    override fun get(
        returnType: Type,
        annotations: Array<Annotation>, retrofit: Retrofit
    ): CallAdapter<*, *>? {
        if (getRawType(returnType) != Call::class.java) {
            return null
        }
        if (returnType !is ParameterizedType) {
            throw IllegalArgumentException(
                "return type must be parameterized as Call<Foo> or Call<? extends Foo>"
            )
        }
        val responseType: Type = getParameterUpperBound(0, returnType)
        if (getRawType(responseType) != ApiResult::class.java) return null

        check(responseType is ParameterizedType) {
            "return type must be ApiResult<*> or ApiResult<out *> for ApiResult<*> check"
        }

        val bodyType = getParameterUpperBound(0, responseType)

        return object : CallAdapter<Any, Any> {
            override fun responseType(): Type {
                return bodyType
            }

            override fun adapt(call: Call<Any>): Any {
                return ResultCallback(call)
            }
        }
    }


    internal class ResultCallback<R : Any>(val delegate: Call<R>) :
        Call<ApiResult<R>> {
        override fun enqueue(callback: Callback<ApiResult<R>>) {
            Objects.requireNonNull(callback, "callback == null")
            if (!ServiceManager.getService<NetworkService>().isNetworkAvailable()) {
                callback.onResponse(
                    this@ResultCallback,
                    Response.success(ApiResult.Failure(NetworkApiException.noInternet()))
                )
            }
            delegate.enqueue(
                object : Callback<R> {
                    override fun onResponse(call: Call<R>, response: Response<R>) {
                        if (delegate.isCanceled) {
                            callback.onResponse(
                                this@ResultCallback,
                                Response.success(ApiResult.Failure(NetworkApiException.noInternet()))
                            )
                        } else {
                            if (response.body() != null) {
                                val body = response.body()!!
                                if (body is RespModel<*> && body.errorCode != 0) {
                                    callback.onResponse(
                                        this@ResultCallback,
                                        Response.success(
                                            ApiResult.Failure(
                                                BizApiException(
                                                    body.errorCode,
                                                    body.errorMsg,
                                                    body
                                                )
                                            )
                                        )
                                    )
                                } else {
                                    callback.onResponse(
                                        this@ResultCallback,
                                        Response.success(ApiResult.Success(response.body()!!))
                                    )
                                }
                            } else {
                                callback.onResponse(
                                    this@ResultCallback,
                                    Response.success(ApiResult.Failure<R>(NetworkApiException.unknown()))
                                )
                            }
                        }
                    }

                    override fun onFailure(call: Call<R>, t: Throwable) {
                        callback.onResponse(
                            this@ResultCallback,
                            Response.success(ApiResult.Failure(NetworkApiException.transform(t)))
                        )
                    }
                })
        }

        override fun isExecuted(): Boolean {
            return delegate.isExecuted
        }

        @Throws(IOException::class)
        override fun execute(): Response<ApiResult<R>> {
            throw UnsupportedOperationException("不支持同步请求")
        }

        override fun cancel() {
            delegate.cancel()
        }

        override fun isCanceled(): Boolean {
            return delegate.isCanceled
        }

        override fun clone(): Call<ApiResult<R>> = ResultCallback(delegate.clone())

        override fun request(): Request {
            return delegate.request()
        }

        override fun timeout(): Timeout {
            return delegate.timeout()
        }
    }


}

